package com.xvyy.tingshu.aspect;

import com.fasterxml.jackson.core.type.TypeReference;
import com.xvyy.tingshu.annotation.Cacheable;
import com.xvyy.tingshu.constant.CacheAbleConstant;
import com.xvyy.tingshu.service.CacheOpsService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * ClassName: CacheAspect
 * Package: com.xvyy.tingshu.search.aspect
 *
 * @Description:
 * @Author: xvyy
 * @Create: 2025/2/9 - 13:57
 * @Version: v1.0
 */
@Component
@Aspect
public class CacheAspect {

    @Autowired
    private RBloomFilter rBloomFilter;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private CacheOpsService cacheOpsService;


    @Around("@annotation(com.xvyy.tingshu.annotation.Cacheable)")
    public Object checkCache(ProceedingJoinPoint pjp) throws Throwable {
        // 1. 定义变量
        // 1.1 获取目标方法对象
        Method method = getMethod(pjp);
        Cacheable methodAnnotation = getMethodAnnotation(pjp, Cacheable.class);
        // 1.2 得到缓存key表达式，计算缓存key表达式值
        String cacheKeyExpression = methodAnnotation.cacheKey();
        String cacheKey = computeCacheKey(cacheKeyExpression, pjp, String.class);
        // 1.2 得到分布式锁key表达式，计算分布式锁key表达式值
        String lockKeyExpression = methodAnnotation.distributedLockKey();
        String lockKey = computeCacheKey(lockKeyExpression, pjp, String.class);
        // 1.3 得到分布式布隆过滤器key表达式，计算分布式布隆过滤器key表达式值
        String bloomKeyExpression = methodAnnotation.distributedBloomKey();
        Long bloomKey = computeCacheKey(bloomKeyExpression, pjp, Long.class);
        // 2. 查询分布式布隆过滤器
        // 2.1 获取分布式布隆过滤器开关
        boolean bloomSwitch = methodAnnotation.distributedBloomSwitch();
        if (!bloomSwitch) {
            // 2.2 没有开启
            boolean bloomContainsFlag = rBloomFilter.contains(bloomKey);
            // 2.1 判断是否存在
            // 2.2 布隆说有，直接返回
            if (!bloomContainsFlag) {
                return null;
            }
            // 2.3 布隆说没有，查询分布式缓存
        }
        // 2.4 开启，查询分布式缓存
        // 3. 查询分布式缓存 (反序列化)
        Object dataFromCache = cacheOpsService.getDataFromCache(cacheKey, new TypeReference<Object>() {
            @Override
            public Type getType() {
                return method.getGenericReturnType();
            }
        });
        if (dataFromCache != null) {
            // 3.1 缓存命中，反序列化成功，直接返回
            return dataFromCache;
        }
        // 3.2 缓存未命中
        // 3.3 获取分布式锁开关
        boolean distributedLockSwitch = methodAnnotation.distributedLockSwitch();
        if (!distributedLockSwitch) {
            // 3.4 没有开启，直接查询数据库
            // 3.4.1 回源查DB
            Object objectFromDb = pjp.proceed(); // 查询数据库
            cacheOpsService.saveDataToCache(cacheKey, objectFromDb);
            return objectFromDb;
        }
        // 3.5 开启分布式锁
        // 4. 获取锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 4.1 加锁（解决缓存击穿问题）
        boolean aBoolean = lock.tryLock();
        if (aBoolean) {
            //4.2 加锁成功
            try {
                // 4.2.1 回源查DB
                Object objectFromDB = pjp.proceed();//这里有异常不要抓，防止多切面兜底逻辑失效
                // 4.2.2 同步数据到缓存中
                cacheOpsService.saveDataToCache(cacheKey, objectFromDB);
                return objectFromDB;
            } finally {
                //4.3 解锁
                lock.unlock();
            }
        } else {
            // 4.4 没抢到锁（等待同步数据时间）
            try {
                Thread.sleep(CacheAbleConstant.SYNC_DATA_TIME);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return cacheOpsService.getDataFromCache(cacheKey, new TypeReference<Object>() {
                @Override
                public Type getType() {
                    return method.getReturnType();
                }
            });
        }
    }

    /**
     * 获取目标方法对象
     *
     * @param pjp
     * @return
     */
    private static Method getMethod(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        return signature.getMethod();
    }

    /**
     * 获取目标方法上的任意类型的注解对象
     *
     * @param pjp
     * @param tClass
     * @param <T>
     * @return
     */
    private static <T extends Annotation> T getMethodAnnotation(ProceedingJoinPoint pjp, Class<T> tClass) {
        return getMethod(pjp).getAnnotation(tClass);
    }

    /**
     * 计算缓存key
     *
     * @param cacheKeyExpression
     * @param pjp
     * @return
     */
    private <T> T computeCacheKey(String cacheKeyExpression, ProceedingJoinPoint pjp, Class<T> valueType) {
        // 1. 创建表达式解析器对象
        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
        // 2. 创建表达式上下文对象
        TemplateParserContext templateParserContext = new TemplateParserContext();
        // 3. 创建计算上下文对象
        StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();
        standardEvaluationContext.setVariable("args", pjp.getArgs());
        // 4. 解析表达式，返回一个Expression对象
        Expression expression = spelExpressionParser.parseExpression(cacheKeyExpression, templateParserContext);
        // 5. 返回结果
        return expression.getValue(standardEvaluationContext, valueType);

    }
}
